<template>
	<div style="position:relative;">
		<div id="parent" class="float-list" v-drag>
			<el-tooltip content="点击后可拖动位置" placement="top">
				<el-button class="list-title" type="primary" v-if="callList.length===0" @click="handleClick">
					<phone style="height: 15px; vertical-align: middle"/>
					呼叫队列 ({{ callList.length }})
				</el-button>
				<el-button class="list-title" type="danger" v-else @click="handleClick">
					<phone style="height: 15px; vertical-align: middle"/>
					呼叫队列 ({{ callList.length }})
				</el-button>
			</el-tooltip>
			<el-scrollbar v-if="listVisible">
				<el-card class="box-card list-item" v-for="call in callList">
					<template #header>
						<div class="card-header" style="height: 22px">
							<el-tag type="danger" size="small">{{ call.level }}级护理</el-tag>
							<span style="margin-left: 20px">{{ call.no }}床</span>
							<span style="margin-left: 20px">{{ call.name }}</span>
						</div>
					</template>
					<div class="footer">
						<el-button type="success" size="mini" @click="answer(call)">接听</el-button>
						<el-button type="danger" size="mini" @click="refuse(call)">拒接</el-button>
					</div>
				</el-card>
			</el-scrollbar>
		</div>
	
	</div>
	<!-- 呼叫弹窗 -->
	<el-dialog v-model="callVisible" width="60%"
			   :destroy-on-close="true"
			   :close-on-click-modal="false"
			   :show-close="false"
			   @close="hangup"
			   :title=title center>
		<Call></Call>
		<div style="text-align: center;">
			<el-button type="primary" :loading="true" plain>音/视频通话中...{{ timeData }}</el-button>
			<el-button type="danger" @click="hangup()" :disabled="hangupDisabled" plain>挂断</el-button>
			<!-- <div class="timeCount">
				<span>{{timeData}}</span>
			</div> -->
		</div>
	</el-dialog>
</template>

<script>

import {computed, onMounted, onUnmounted, reactive, ref} from 'vue';
import {deviceId, Socket} from "../utils/websocket_hb";
import Call from "../components/Call.vue";
import {ElMessage} from "element-plus";
import {Phone} from "@element-plus/icons";
import {ring} from "../utils/ring";
import {callRecord, signal, find, findByQno} from "../api/call";
import {useStore} from "vuex";
import store from "../store";
import router from "../router";
import {useRouter} from "vue-router";

const websocket = Socket();

export default {
	components: {Call, Phone},
	name: 'FloatList',
	setup() {
		//获取窗口宽高
		const getScrollHeight = () => {
			return document.body.clientHeight * 0.65
		}
		const scrollHeight = ref(getScrollHeight())
		const windowWidth = ref(document.body.clientHeight)
		const windowHeight = ref(document.body.clientWidth)
		const windowResize = () => {
			scrollHeight.value = getScrollHeight()
			windowHeight.value = document.body.clientHeight
			windowWidth.value = document.body.clientWidth
		}
		onMounted(() => {
			window.addEventListener('resize', windowResize)
		})
		onUnmounted(() => {
			window.removeEventListener('resize', windowResize)
		});
		
		
		//禁用挂断按钮
		const hangupDisabled = ref(true);
		//呼叫模式：是否支持视频
		let constraints = {
			video: true,
			audio: {
				// 设置回音消除
				noiseSuppression: true,
				// 设置降噪
				echoCancellation: true,
			}
		};
		//列表是否显示
		const listVisible = ref(false);
		//呼叫弹窗是否显示
		const callVisible = ref(false);
		//const nurseCallVisible=computed(() => store.state.nurseCallVisible)
		//接听的病号
		let title = ref(null);
		
		//TODO //chj callRecord and time_meter相关
		const callRecordParams = reactive({
			startTime: "",
			endTime: "",
			callName: "",
			calledName: "",
			callId: "",
			calledId: "",
			status: "",
		})
		
		//TODO chj 通话弹窗计时显示
		let timeData = ref("");
		let timer = null;
		const timeCount = () => {
			// 会议计时
			let tempTime = 0;
			let second = 0;
			let minute = 0;
			let hour = 0;
			timer = setInterval(() => {
				tempTime += 1;
				hour = parseInt(tempTime / 3600); // 转小时
				minute = parseInt(tempTime % 3600 / 60); // 转分钟
				second = parseInt(tempTime % 3600 % 60); // 转秒
				// 格式化
				hour = (hour > 9 ? hour : "0" + hour) + "";
				minute = (minute > 9 ? minute : "0" + minute) + "";
				second = (second > 9 ? second : "0" + second) + "";
				timeData.value = hour + ":" + minute + ":" + second;
			}, 1000);
		}
		
		//接听
		const answer = (item) => {
			//确定是否需要开启视频
			constraints.video = item.video;
			
			title.value = "与病人通话：" + item.no;
			callVisible.value = true;
			
			//TODO: 将点击的请求从通话队列中移除
			callList.value.splice(callList.value.indexOf(item), 1);
			sessionStorage.setItem('callList', JSON.stringify(callList.value));
			
			//TODO: 请求信号接口：接听
			const qno = item.no;
			const p = findByQno(qno);
			if (p === null) return
			signal(p.bedId, false, 'call');
			
			startHandle().then(() => {
				store.state.ws.send(JSON.stringify({
					'type': 'start',
					'message': {
						'userId': userId,
						'toUserId': item.deviceId
					}
				}))
				timeCount()
				callRecordParams.startTime = Math.floor(new Date().getTime() / 1000) * 1000
				callRecordParams.callId = item.deviceId
				callRecordParams.calledId = userId
				callRecordParams.calledName = "station"
				callRecordParams.callName = item.name
				callRecordParams.status = "已挂断"
				//3s后允许挂断
				setTimeout(() => {
					hangupDisabled.value = false;
				}, 4000);
			})
				.catch(() => {
					ElMessage.error('请检查是否处于局域网环境下！');
				})
		}
		//拒接
		const refuse = (item) => {
			//TODO: 将点击的请求从通话队列中移除
			console.log('当前拒绝的用户：', item)
			console.log('当前队列：', callList.value)
			callList.value.splice(callList.value.indexOf(item), 1);
			sessionStorage.setItem('callList', JSON.stringify(callList.value));
			console.log('拒绝后队列：', callList.value)
			toUserId.value = item.deviceId
			
			//TODO: 请求信号接口：挂断
			const qno = item.no;
			const p = findByQno(qno);
			if (p === null) return
			signal(p.bedId, false, 'call');
			
			store.state.ws.send(JSON.stringify({
				'type': 'refuse',
				'message': {
					'userId': userId,
					'toUserId': toUserId.value
				}
			}))
			callRecordParams.startTime = Math.floor(item.time / 1000) * 1000
			callRecordParams.endTime = Math.floor(new Date().getTime() / 1000) * 1000
			callRecordParams.callId = item.deviceId
			callRecordParams.calledId = userId
			callRecordParams.calledName = "station"
			callRecordParams.callName = item.name
			callRecordParams.status = "已挂断"
			callRecord(callRecordParams).then((res) => {
			})
			ElMessage.success('已挂断！');
		}
		
		//初始化数据
		let callList = ref();
		if (!sessionStorage.getItem('callList')) {
			callList.value = [];
			sessionStorage.setItem('callList', JSON.stringify(callList.value));
		} else {
			//JSON字符串转换为数组
			callList.value = eval('(' + sessionStorage.getItem('callList') + ')');
		}
		
		//固定生成自己的用户id
		let userId = deviceId;
		
		let toUserId = ref(null);
		let localVideo = null;
		let remoteVideo = null;
		
		//使用google公开stun服务器
		const config = {iceServers: [{urls: 'stun:stun.l.google.com:19302'}]};
		
		let peerConnection = new webkitRTCPeerConnection(null);
		//let socket=computed(store.state.ws);
		let localStream = ref();
		
		//心跳
		let lockReconnect = false;
		let timeInterval;
		
		function Reconnect() {
			if (lockReconnect) {
				return;
			}
			lockReconnect = true;
			timeInterval && clearTimeout(timeInterval);
			timeInterval = setTimeout(function () {
				console.log("websocket正在重连")
				startWebsocket();
				lockReconnect = false;
			}, 4000)
		}
		
		let heartCheck = {
			timeout: 60 * 1000,
			timoutObj: null,
			//重置倒计时
			reset: function () {
				//console.log("心跳重置")
				clearTimeout(this.timeoutObj);
				this.startLoop();
			},
			startLoop: function () {
				let self = this
				this.timeoutObj = setTimeout(function () {
					try {
						store.state.ws.send(JSON.stringify({
							'type': 'pong'
						}));
						self.reset();
					} catch (e) {
						console.log("检测到异常，进行重连", e)
						Reconnect();
					}
				}, this.timeout)
			}
		}
		
		//连接服务器
		startWebsocket();
		
		//连接到WebSocket服务器
		function startWebsocket() {
			//let webSocketUrl = 'wss://' + location.hostname + '/call/' + userId
			//let webSocketUrl = 'wss://192.168.188.111/call/' + userId
			store.state.ws = websocket;
			heartCheck.startLoop();
			store.state.ws.onopen = function () {
				console.log('websocket连接成功！');
			};
			//连接关闭
			store.state.ws.onclose = function (e) {
				console.log('websocket连接关闭！' + e.code + ' ' + e.reason + ' ' + e.wasClean);
				Reconnect();
			};
			//连接出错
			store.state.ws.onerror = function (e) {
				console.log(e);
				console.error('websocket连接失败！');
				Reconnect();
			};
			store.state.ws.onmessage = onmessage;
			//store.state.ws=socket;
		}
		
		//开启本地的媒体设备并展示
		async function startHandle() {
			await navigator.mediaDevices.getUserMedia(constraints)
				//展示本地视频流
				.then(getLocalMediaStream)
				.catch((err) => {
					console.log("呼叫报错了", err)
					ElMessage.error('无摄像头/麦克风设备或无访问权限！');
				});
			//创建点对点连接对象
			createConnection();
		}
		
		//获得流后将音视频流展示并保存到变量
		function getLocalMediaStream(mediaStream) {
			localVideo = document.getElementById('localVideo');
			remoteVideo = document.getElementById('remoteVideo');
			console.log("localVideo:", localVideo)
			localVideo.srcObject = mediaStream;
			localStream.value = mediaStream;
		}
		
		//创建点对点连接对象
		function createConnection() {
			//peerConnection = new RTCPeerConnection(config);
			//peerConnection = new RTCPeerConnection(null);
			//peerConnection = new webkitRTCPeerConnection(null)
			if (localStream.value) {
				//视频轨道
				const videoTracks = localStream.value.getVideoTracks();
				//音频轨道
				const audioTracks = localStream.value.getAudioTracks();
				//判断视频轨道是否有值
				if (videoTracks.length > 0) {
					console.log('使用的设备为：', videoTracks[0].label);
				}
				//判断音频轨道是否有值
				if (audioTracks.length > 0) {
					console.log('使用的设备为：', audioTracks[0].label);
				}
				//本地视频流添加到远程连接
				localStream.value.getTracks().forEach((track) => {
					peerConnection.addTrack(track, localStream.value);
				});
				console.log('本地音视频流添加到对等连接成功！');
			}
			// 监听返回的 Candidate
			peerConnection.addEventListener('icecandidate', handleConnection);
			// 监听 ICE 状态变化
			peerConnection.addEventListener('iceconnectionstatechange', handleConnectionChange);
			//拿到流的时候调用
			peerConnection.addEventListener('track', getRemoteMediaStream);
		}
		
		//进行通信
		function onmessage(e) {
			heartCheck.reset();
			const data = JSON.parse(e.data)
      const type=data.type
      if (type==='ping'){
        store.state.ws.send(JSON.stringify({
          'type': 'pong'
        }))
        return
      }
			const message = data.message
			toUserId.value = message.userId
			const myDate = new Date();
			switch (type) {
				// case 'ping':
        //   console.log(data)
				//
				// 	break;
				case 'tell':
					store.state.ws.send(JSON.stringify({
						'type': 'connect',
						'message': {
							'userId': userId,
							'toUserId': toUserId.value,
							'video': message.video
						}
					}));
					break;
				case 'connect':
					//TODO: 查找病人
					const patient = find(toUserId.value);
					console.log('connect时根据deviceId查找病人：', patient)
					
					//加到呼叫队列
					let videoOn = message.video;
					ElMessage.warning(patient.queNumber + '号床发来呼叫！');
					// refresh()
					//来电铃声提示
					ring();
					let call = callList.value.find(call => call.no === patient.queNumber);
					console.log('connect时队列：', callList.value)
					console.log('connect时在队列中查找病人', call)
					if (typeof (call) == 'undefined') {
						callList.value.push({
							no: patient.queNumber,
							deviceId: toUserId.value,
							name: patient.name,
							level: patient.level,
							time: myDate.getTime(),
							video: videoOn,
						});
						sessionStorage.setItem('callList', JSON.stringify(callList.value));
					}
					
					sortCall();
					break;
				case 'start':
					//同意连接之后开始连接
					startConnection();
					break;
				case 'offer':
					peerConnection.setRemoteDescription(new RTCSessionDescription({
						type: 'offer',
						sdp: message.sdp
					}))
						.then(() => {
							console.log('设置远程sdp信息成功！');
						})
						.catch((err) => {
							console.error('设置远程sdp信息失败', err);
						});
					
					peerConnection.createAnswer()
						.then(function (answer) {
							peerConnection.setLocalDescription(answer)
								.then(() => {
									console.log('设置本地answer成功!');
								})
								.catch((err) => {
									console.error('设置本地answer失败：', err);
								});
							store.state.ws.send(JSON.stringify({
								'type': 'answer',
								'message': {
									'userId': userId,
									'toUserId': toUserId.value,
									'sdp': answer.sdp
								}
							}))
						}).catch(e => {
						console.error(e)
					});
					break;
				case 'answer':
					peerConnection.setRemoteDescription(new RTCSessionDescription({
						type: 'answer',
						sdp: message.sdp
					}))
						.then(() => {
							console.log('设置远程answer成功');
						})
						.catch((err) => {
							console.error('设置远程answer失败：', err);
						});
					break;
				case 'icecandidate':
					// 创建 RTCIceCandidate 对象
					let newIceCandidate = new RTCIceCandidate({
						sdpMid: message.candidate.sdpMid,
						sdpMLineIndex: message.candidate.sdpMLineIndex,
						candidate: message.candidate.sdp
					});
					// 将本地获得的 Candidate 添加到远端的 RTCPeerConnection 对象中
					peerConnection.addIceCandidate(newIceCandidate)
						.then(() => {
							console.log('本地IceCandidate添加成功');
						})
						.catch((error) => {
							console.error('本地IceCandidate添加失败：', error);
						});
					break;
				case 'cancel':
					store.state.ws.send(JSON.stringify({
						'type': 'hungup',
						'message': {
							'userId': userId,
							'toUserId': toUserId.value
						}
					}));
					for (let call of callList.value) {
						if (call.no === find(toUserId.value).queNumber) {
							callList.value.splice(callList.value.indexOf(call), 1);
							sessionStorage.setItem('callList', JSON.stringify(callList.value));
						}
					}
					break;
				case 'hangup':
					//告知服务器被挂断！！！
					closeDialog()
					store.state.ws.send(JSON.stringify({
						'type': 'hungup',
						'message': {
							'userId': userId,
							'toUserId': toUserId.value
						}
					}));
					for (let call of callList.value) {
						if (call.no === find(toUserId.value).queNumber) {
							console.log(call)
							callList.value.splice(callList.value.indexOf(call), 1);
							sessionStorage.setItem('callList', JSON.stringify(callList.value));
						}
					}
					refresh()
				
				//callVisible.value=false
				//
				//强制刷新
				//location.reload();
				// //停止音视频流传输
				
				//       localStream.value.getTracks().forEach((track) => {
				//     track.stop();
				// });
				//
				// //关闭连接并设置为空
				// peerConnection.close();
				// peerConnection = new webkitRTCPeerConnection(null);
				default:
					break;
			}
		}
		
		//创建sdp准备建立连接
		function startConnection() {
			//发送offer
			peerConnection.createOffer()
				.then(description => {
					// 将 offer 保存到本地
					peerConnection.setLocalDescription(description)
						.then(() => {
							console.log('设置本地sdp信息成功！');
							// 本地设置描述并将它发送给远端
							store.state.ws.send(JSON.stringify({
								'type': 'offer',
								'message': {
									'userId': userId,
									'toUserId': toUserId.value,
									'sdp': description.sdp
								}
							}))
						}).catch((err) => {
						console.error('设置本地sdp信息失败：', err)
					});
				})
				.catch((err) => {
					console.error('创建offer失败：', err);
				});
		}
		
		//挂断通话
		function hangup() {
			callVisible.value = false;
			//向服务器发送挂断信号
			store.state.ws.send(JSON.stringify({
				'type': 'hangup',
				'message': {
					'userId': userId,
					'toUserId': toUserId.value
				}
			}));
			
			//TODO: 请求信号接口：挂断
			const patient = find(toUserId.value);
			signal(patient.bedId, false, "call");
			
			callRecordParams.endTime = Math.floor(new Date().getTime() / 1000) * 1000
			callRecord(callRecordParams).then((res) => {
				console.log("callRecord111-----------------===>", res)
			})
			clearInterval(timer)
			console.log("清除计时器----------------------")
			//强制刷新
			location.reload();
			//停止音视频流传输
			// localStream.value.getTracks().forEach((track) => {
			//     track.stop();
			// });
			// //关闭连接并设置为空
			// peerConnection.close();
			// peerConnection = new webkitRTCPeerConnection(null);
		}
		
		//端与端建立连接
		function handleConnection(event) {
			// 获取到触发 icecandidate 事件的 RTCPeerConnection 对象
			// 获取到具体的Candidate
			const peerConnection = event.target;
			const icecandidate = event.candidate;
			
			if (icecandidate) {
				store.state.ws.send(JSON.stringify({
					'type': 'icecandidate',
					'message': {
						'userId': userId,
						'toUserId': toUserId.value,
						'candidate': {
							sdp: event.candidate.candidate,
							sdpMid: event.candidate.sdpMid,
							sdpMLineIndex: event.candidate.sdpMLineIndex
						}
					}
				}))
			}
		}
		
		//显示远端媒体流
		function getRemoteMediaStream(event) {
			if (event.streams[0]) {
				remoteVideo.srcObject = event.streams[0];
			}
			//播放远端视频
			//remoteVideo.play();
		}
		
		//监听ice状态变化
		function handleConnectionChange(event) {
			const connection = event.target;
			console.log('ICE当前状态：', connection.iceConnectionState);
			//重连ice
			if (connection.iceConnectionState === 'failed' || connection.iceConnectionState === 'disconnected') {
				peerConnection.restartIce();
			}
			// if (connection.iceConnectionState === 'disconnected') {
			// 	//停止音视频流传输
			// 	localStream.value.getTracks().forEach((track) => {
			// 		track.stop();
			// 	});
			// 	//关闭连接并设置为空
			// 	peerConnection.close();
			// 	peerConnection = new RTCPeerConnection(null);
			// }
		}
		
		//监听按钮点击
		function handleClick() {
			// 点击事件触发时，判断当前状态是拖拽还是点击，若是拖拽，直接返回不继续执行
			const isDrag = document.getElementById('parent').getAttribute('drag-flag')
			if (isDrag == 'true') {
				return
			}
			listVisible.value = !listVisible.value
		}
		
		//对List排序，首先按level大的，level相等按time小的
		function sortCall() {
			callList.value.sort((a, b) => {
				if (a.level == b.level) {
					return a.time - b.time;
				} else {
					return b.level - a.level;
				}
			})
		}
		
		sortCall();
		
		function closeDialog() {
			callVisible.value = false;
			store.state.nurseCallVisible = false;
		}
		
		function refresh() {
			router.replace('/refresh')
		}
		
		//根据deviceId获取病人level
		// function getLevel(deviceId) {
		//     for (let i = 0; i < patientData.value.length; i++) {
		//         for (let j = 0; j < patientData.value[i].length; j++) {
		//             let t = patientData.value[i][j]
		//             if (t.deviceId == deviceId) return t.level;
		//         }
		//     }
		// }
		
		
		return {
			callList,
			scrollHeight,
			windowHeight,
			windowWidth,
			callVisible,
			listVisible,
			handleClick,
			//socket,
			//websocket,
			title,
			startHandle,
			answer,
			refuse,
			hangup,
			
			timeData,
			hangupDisabled,
			refresh
		}
	},
	directives: {
		drag(el) {
			let oDiv = el // 当前元素
			let firstTime = ''
			let lastTime = ''
			oDiv.onmousedown = function (e) {
				document.getElementById('parent').setAttribute('drag-flag', false)
				firstTime = new Date().getTime()
				// 鼠标按下，计算当前元素距离可视区的距离
				let disX = e.clientX - oDiv.offsetLeft
				let disY = e.clientY - oDiv.offsetTop
				document.onmousemove = function (e) {
					// 通过事件委托，计算移动的距离
					let l = e.clientX - disX
					let t = e.clientY - disY
					// 移动当前元素
					oDiv.style.left = l + 'px'
					oDiv.style.top = t + 'px'
					lastTime = new Date().getTime()
					if (lastTime - firstTime > 200) {
						document.getElementById('parent').setAttribute('drag-flag', true)
					}
				}
				//鼠标抬起清除事件
				document.onmouseup = function (e) {
					document.onmousemove = null
					document.onmouseup = null
				}
				// return false不加的话可能导致黏连，就是拖到一个地方时div粘在鼠标上不下来，相当于onmouseup失效
				return false
			}
		}
	}
}
</script>

<style scoped>
.float-list {
	position: absolute;
	right: 10px;
	top: 100px;
	cursor: pointer;
	z-index: 888;
}

.list-title {
	border-radius: 1px;
	box-shadow: 5px 5px 5px 5px rgba(0, 0, 0, 0.5);
	width: 200px;
}

.icon {
	font-size: 16px;
}

.list-item {
	width: 200px;
}

:root {
	--el-color-primary: #409eff;
}

.footer {
	display: flex;
	justify-content: space-between;
}

</style>
